/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package xenon3d;

import java.awt.BufferCapabilities;
import java.awt.GraphicsConfigTemplate;
import java.awt.GraphicsConfiguration;
import java.awt.ImageCapabilities;
import java.awt.color.ColorSpace;
import java.awt.image.ColorModel;
import javax.media.opengl.GLCapabilities;


/**
 * This class is used to obtain a valid GraphicsConfiguration that can be used
 * by Xenon3D. A user should instantiate this template and and then set all
 * non-default attributes as desired. The getBestConfiguration() method in the
 * GraphicsDevice class is then called with this GraphicsConfigTemplate and the
 * "best" GraphicsConfiguration is returned. The "best" GraphicsConfiguration
 * means that this GraphicsConfiguration is supported and it meets or exceeds
 * what was requested in the GraphicsConfigTemplate. Null is returned if no such
 * "best" GraphicsConfiguration is found.
 * @author Volker Everts
 * @version 0.1 - 13.08.2011: Created
 */
public class GraphicsConfigTemplate3D extends GraphicsConfigTemplate {
    
    // <editor-fold defaultstate="collapsed" desc=" Static Attributes ">
    
    /** The latest "best" graphics configuration. */
    static GraphicsConfiguration bestGC;
    
    /** The latest "best" GLCapabilities. */
    static GLCapabilities bestCaps;
    
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc=" Private Fields ">
    
    /** The serial version UID. */
    private static final long serialVersionUID = 20113107L;
    
    /** The internal GLCapabilities object. */
    private GLCapabilities caps;

    /** The fullscreen flag. */
    private boolean fullscreen;

    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc=" Initialization ">

    /**
     * Creates a new GraphicsConfigTemplate3D with default parameters.
     */
    public GraphicsConfigTemplate3D() {
        caps = new GLCapabilities(Xenon3D.getProfile());
        caps.setAccumAlphaBits(0);
        caps.setAccumRedBits(0);
        caps.setAccumGreenBits(0);
        caps.setAccumBlueBits(0);
        caps.setAlphaBits(0);
        caps.setRedBits(8);
        caps.setGreenBits(8);
        caps.setBlueBits(8);
        caps.setDepthBits(24);
        caps.setDoubleBuffered(true);
        caps.setHardwareAccelerated(true);
        caps.setStencilBits(0);
        caps.setStereo(false);
        caps.setSampleBuffers(false);
        caps.setNumSamples(2);
    }

    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc=" Public Properties ">
    
    /**
     * Sets the color buffer size requirement. The default is 32.
     * @param size the required color buffer size, must be one of 16 or 32
     */
    public void setColorSize(int size) {
        if (size == 16) {
            caps.setRedBits(5);
            caps.setGreenBits(6);
            caps.setBlueBits(5);
        }
        else if (size == 32) {
            caps.setRedBits(8);
            caps.setGreenBits(8);
            caps.setBlueBits(8);
        }
        else throw new IllegalArgumentException();
    }
    
    /**
     * Gets the color buffer size requirement.
     * @return the required color buffer size, one of 16 or 32
     */
    public int getColorSize() {
        return caps.getRedBits() == 8 ? 32 : 16;
    }
    
    /**
     * Sets the depth buffer size requirement. The default is 16.
     * @param size the required depth buffer size, one of 16 or 24
     */
    public void setDepthSize(int size) {
        if (size == 16 || size == 24) caps.setDepthBits(size);
        else throw new IllegalArgumentException();
    }
    
    /**
     * Gets the depth buffer size requirement.
     * @return the required depth buffer size
     */
    public int getDepthBufferSize() {
        return caps.getDepthBits();
    }
    
    /**
     * Sets the double buffering requirement. The default is true.
     * @param enable if true, double buffering is required
     */
    public void setDoubleBuffer(boolean enable) {
        caps.setDoubleBuffered(enable);
    }
    
    /**
     * Gets the double buffering requirement.
     * @return true, if double buffering is required
     */
    public boolean isDoubleBuffer() {
        return caps.getDoubleBuffered();
    }
    
    /**
     * Sets the hardware acceleration requirement. The default is true.
     * @param enable if true, hardware acceleation is required
     */
    public void setHardwareAcceleration(boolean enable) {
        caps.setHardwareAccelerated(enable);
    }
    
    /**
     * Gets the hardware acceleration requirement.
     * @return true, if hardware acceleration is required
     */
    public boolean isHardwareAcceleration() {
        return caps.getHardwareAccelerated();
    }
    
    /**
     * Sets the stencil buffer requirement. The default is false.
     * @param enable if true, a stencil buffer is required
     */
    public void setStencilBuffer(boolean enable) {
        caps.setStencilBits(enable ? 8 : 0);
    }
    
    /**
     * Gets the stencil buffer requirement.
     * @return true, if a stencil buffer is required
     */
    public boolean isStencilBuffer() {
        return caps.getStencilBits() != 0;
    }
    
    /**
     * Sets the stereo requirement. The default is false.
     * <p>
     * NOTE: setting stereo to true is not yet supported
     * @param enable if true, stereo rendering is required
     */
    public void setStereo(boolean enable) {
        // TODO: Stereo rendering
        if (enable) throw new UnsupportedOperationException();
        caps.setStereo(enable);
    }
    
    /**
     * Gets the stereo requirement.
     * @return true, if stereo rendering is required
     */
    public boolean isStereo() {
        return caps.getStereo();
    }
    
    /**
     * Sets the scene antialiasing requirement. The default is false.
     * @param enable if true, full scene antialiasing is required
     */
    public void setSceneAntialiasing(boolean enable) {
        caps.setSampleBuffers(enable);
    }
    
    /**
     * Gets the scene antialiasing requirement.
     * @return true, if full scene antialiasing is required
     */
    public boolean isSceneAntialiasing() {
        return caps.getSampleBuffers();
    }
    
    /**
     * Sets the number of sample buffers allocated when full scene antialiasing
     * is required. Default is 2. Allowed values are in the range 1 to 4.
     * @param n the number of sample buffers
     */
    public void setNumSamples(int n) {
        if (n < 0 || n > 4) throw new IllegalArgumentException();
        caps.setNumSamples(n);
    }
    
    /**
     * Gets the number of sample buffers allocated when full scene antialiasing
     * is required.
     * @return the number of sample buffers
     */
    public int getNumSamples() {
        return caps.getNumSamples();
    }
    
    /**
     * Gets the fullscreen requirement.
     * @return true, if fullscreen exclusive mode is requested
     */
    public boolean isFullScreen() {
        return fullscreen;
    }
    
    /**
     * Sets the fullscreen requirement.  The default is false.
     * @param enable if true, fullscreen will be enabled
     */
    public void setFullScreen(boolean enable) {
        fullscreen = enable;
    }

    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc=" Public Methods ">
    
    /**
     * Returns a boolean indicating whether or not the given GraphicsConfiguration
     * can be used to create a drawing surface that can be rendered to.
     * @param gc the graphics configuration under test
     * @return true, if the specified graphics configuration is supported
     */
    @Override
    public boolean isGraphicsConfigSupported(GraphicsConfiguration gc) {
        // Check for valid gc
        if (gc == null) return false;
        // Check color size
        ColorModel cm = gc.getColorModel();
        ColorSpace cs = cm.getColorSpace();
        if (!cs.isCS_sRGB()) return false;
        int[] rgb = cm.getComponentSize();
        int red = rgb[0];
        int green = rgb[1];
        int blue = rgb[2];
        int alpha = 0;
        if (rgb.length > 3) alpha = rgb[3];
        int colorSize = getColorSize();
        if (colorSize == 16) {
            if (red != 5) return false;
            if (green != 6) return false;
            if (blue != 5) return false;
        }
        else {
            if (red != 8) return false;
            if (green != 8) return false;
            if (blue != 8) return false;
        }
        // Check double buffer
        BufferCapabilities bc = gc.getBufferCapabilities();
        if (caps.getDoubleBuffered() && !bc.isPageFlipping()) return false;
        if (caps.getStereo() && !bc.isMultiBufferAvailable()) return false;
        if (!fullscreen && bc.isFullScreenRequired()) return false;
        // Check hardware acceleration
        ImageCapabilities ic = bc.getBackBufferCapabilities();
        if (caps.getHardwareAccelerated() && !ic.isAccelerated()) return false;
        if (fullscreen && !gc.getDevice().isFullScreenSupported()) return false;
        // Default: OK
        return true;
    }
    
    /**
     * Implement the abstract function of getBestConfiguration() of class
     * GraphicsConfigTemplate. Usually this function is not directly called by
     * the user. It is implicitly called by getBestConfiguration() in class
     * GraphicsDevice. The method getBestConfiguration() in GraphicsDevice will
     * return whatever this function returns. This function will return the
     * "best" GraphicsConfiguration. The "best" GraphicsConfiguration means that
     * this GraphicsConfiguration is supported and it meets or exceeds what was
     * requested in the GraphicsConfigTemplate. If no such "best" configuration
     * is found, null is returned. 
     * @param gc the array of GraphicsConfigurations to choose from
     * @return the best GraphicsConfiguration
     */
    @Override
    public GraphicsConfiguration getBestConfiguration(GraphicsConfiguration[] gc) {
        bestGC = null;
        bestCaps = null;
        if (gc == null || gc.length == 0) return null;
        for (GraphicsConfiguration c : gc) {
            if (isGraphicsConfigSupported(c)) {
                bestGC = c;
                bestCaps = caps;
                break;
            }
        }
        return bestGC;
    }

    // </editor-fold>
    
} // end class GraphicsConfigTemplate3D